using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
using uTILLIty.Rave.Contract.Configuration;

namespace uTILLIty.Rave.Contract.StorageAdapter
{
  /// <summary>
  /// Implementors of this Interface can apply the <see cref="Configuration"/> to a target repository
  /// (such as the File-System, Sharepoint, Wiki-Site, Database, etc.)
  /// </summary>
  /// <remarks>
  /// <para>Due to the simulation-feature implemented within the calling logic, the Adapter should not 
  /// cause any change of data in the store in any validation or Check-methods. Only when the explicit
  /// Create-, Apply- or Update- methods are called should the implementor update the store.</para>
  /// <para>Remember to register your validation-schemas with the container (as type XmlSchema)</para>
  /// </remarks>
  /// <seealso cref="BaseStorageAdapter"/>
  public interface IStorageAdapter
  {
    /// <summary>
    /// A service for resolving member-references. An implementation is provided by the calling logic
    /// </summary>
    IMemberResolver MemberResolver { set; }

  	/// <summary>
  	/// A source (supplied from the ConfigurationManager) for logging/tracing the execution of the adapter
  	/// </summary>
  	TraceSource TraceSource { get; set; }

    /// <summary>
    /// Returns the name, as registered, of the system this instance supports
    /// </summary>
    string TargetSystem { get; }

  	#region Validation

    /// <summary>
    /// Validates the general <see cref="uTILLIty.Rave.Contract.Configuration.Configuration.StorageAdapterConfiguration"/> information
    /// </summary>
    /// <param name="configuration">the XML structure provided within the configuration</param>
    /// <returns>a list of errors found</returns>
    string[] ValidateAdapterConfiguration(XmlNode configuration);

    /// <summary>
    /// Validates the well-formedness and syntax of the (absolute) root-path defined in the <see cref="Configuration"/>
    /// </summary>
    /// <param name="path">the root-path</param>
    /// <returns>a list of errors found</returns>
    string[] ValidateRootPath(string path);

    /// <summary>
    /// Validates the storage-specific configuration-information of the supplied <see cref="DirectoryContext"/>.
    /// Processing of <see cref="Directory.SubDirectories"/> and <see cref="Directory.SecurityEntries"/> is not required
    /// </summary>
    /// <remarks>
    /// At least the <see cref="Directory.Name"/> should be checked for well-formedness and length constraints 
    /// for the target store
    /// </remarks>
    /// <returns>a list of errors found</returns>
    string[] ValidateDirectory(DirectoryContext directoryContext);

    /// <summary>
    /// Validates the storage-specific configuration-information of the supplied <see cref="SecurityEntry"/>.
    /// </summary>
    /// <returns>a list of errors found</returns>
    string[] ValidateSecurityEntry(SecurityEntry securityEntry);

    /// <summary>
    /// Validates the storage-specific configuration-information of the supplied <see cref="Member"/>.
    /// The member does not have to be resolved at this time! Just a configuration-check.
    /// </summary>
    /// <returns>a list of errors found</returns>
    string[] ValidateMember(Member member);

    #endregion

    /// <summary>
    /// Check, wether the specified <see cref="Directory"/> exists in the store. 
    /// </summary>
    /// <remarks>
    /// Implementors should also account for a situation, where the directory exists, but cannot be accessed
    /// by the current caller!
    /// </remarks>
    /// <param name="directoryContext">The directory to check for existence in the store</param>
    /// <returns>True, if the directory exists, otherwise False</returns>
    /// <seealso cref="CreateDirectory"/>
    bool CheckDirectoryExists(DirectoryContext directoryContext);
    
    /// <summary>
    /// Try to create the directory in the store at the <see cref="DirectoryContext.ParentDirectory">specified</see> 
    /// position. If it cannot be created, an exception should be thrown.
    /// </summary>
    /// <param name="directoryContext">The directory to create</param>
    /// <seealso cref="CheckDirectoryExists"/>
    void CreateDirectory(DirectoryContext directoryContext);

    /// <summary>
    /// Returns a list of all existing <see cref="SecurityEntryContext">ACL entries</see> of the directory
    /// specified, including all rights and member information
    /// </summary>
    /// <param name="directoryContext">The directory to retrieve the ACL entries for</param>
		/// <param name="containsInheritedAcls">Returned by the implementation to indicate wether additional, inherited ACL-entries exist on the target directory</param>
		List<SecurityEntryContext> GetExistingSecurityEntries(DirectoryContext directoryContext, out bool containsInheritedAcls);

		/// <summary>
		/// Checks in the backend store, wether the supplied identity is a group or user
		/// </summary>
		/// <param name="identity">an Identifier (SamAccountName, SID, etc)</param>
		/// <returns>the <see cref="MemberType"/> resembled by the specified identity</returns>
		MemberType GetMemberType(string identity);

    /// <summary>
    /// Tries to resolve the <paramref name="member"/> within the target stores' rights-system. This method should NEVER
    /// create the <paramref name="member"/> in question! The calling logic will cache the returned member and always
    /// use the <see cref="StorageMember.ID"/> to uniquely identify it in further processing
    /// </summary>
    /// <param name="member">The member to resolve</param>
    /// <returns></returns>
    /// <seealso cref="CreateMember"/>
    StorageMember TryResolveMember(Member member);

    /// <summary>
    /// Tries to create the member by interpreting the <see cref="Member.CreationConfiguration"/>. 
    /// </summary>
    /// <remarks>
    /// The calling logic will only call this method, if a <see cref="Member.CreationConfiguration"/> as been supplied
    /// in the configuration
    /// </remarks>
    /// <param name="member">The member to create</param>
    /// <returns>A reference to the created member, as if it had been <see cref="TryResolveMember">resolved</see></returns>
    /// <seealso cref="TryResolveMember"/>
    StorageMember CreateMember(Member member);

    /// <summary>
    /// Ensures that an existing directory's ACL inheritance has been deactivated
    /// </summary>
    /// <remarks>
    /// The calling logic will only call this method, if <see cref="Directory.RemoveSecurityEntriesInheritance"/> has been
    /// set to True in the configuration for the supplied <paramref name="directoryContext">directory</paramref>
    /// </remarks>
    /// <param name="directoryContext">The directory to remove ACL inheritance from</param>
    /// <param name="inheritance">wether to turn inheritance on (true) or off (false)</param>
    /// <seealso cref="Directory"/>
    /// <seealso cref="Directory.RemoveSecurityEntriesInheritance"/>
    void SetSecurityEntriesInheritance(DirectoryContext directoryContext, bool inheritance);

		/// <summary>
		/// Checks wether the security-inheritance is currently enabled on the supplied <paramref name="directoryContext">directory</paramref>
		/// </summary>
		/// <returns>True, if security-inheritance is enabled, else False</returns>
		/// <seealso cref="SetSecurityEntriesInheritance"/>
		bool IsSecurityEntriesInheritanceEnabled(DirectoryContext directoryContext);

    /// <summary>
    /// Applies the <see cref="SecurityEntryContext.Action">changes</see> defined within each <see cref="SecurityEntryContext"/>
    /// to the ACL of the target store
    /// </summary>
    /// <param name="directoryContext">The directory who's ACLs are to be altered</param>
    /// <param name="securityChangesToApply">A list of ACL entries apply changes for</param>
    void ApplySecurityChanges(DirectoryContext directoryContext, List<SecurityEntryContext> securityChangesToApply);

    /// <summary>
    /// Parses and compares 2 rights supplied.
    /// </summary>
    /// <remarks>
    /// Depending on the adapter, the rights are usually a serialised representation of a Flags-Enumeration 
    /// (komma-separated list of Enum entries). So they should be parsed accordingly and compared for semantic differences,
    /// IGNORING any change of order, as long as this makes no difference to the target store
    /// </remarks>
    /// <param name="rights1">the rights specified in the existing ACL entry</param>
    /// <param name="rights2">the rights specified in the configuration</param>
    /// <returns>True, if the 2 rights differ semantically, otherwise False</returns>
    bool DoRightsDiffer(string rights1, string rights2);

		/// <summary>
		/// Merges the supplied rights together
		/// </summary>
		/// <param name="rights1"></param>
		/// <param name="rights2"></param>
		/// <returns>A merged version of both rights supplied</returns>
		/// <remarks>
		/// This function is called, when a configuration refers to one security-entry multiple times
		/// </remarks>
  	string MergeRights(string rights1, string rights2);
    
    /// <summary>
    /// Parses the given <paramref name="path"/> and splits the directory from it's parent
    /// </summary>
    /// <param name="path">the path (in store notation) to parse/split</param>
    /// <param name="parentPath">the parent path, if any, or null</param>
    /// <returns>the name of the current directory</returns>
    /// <example>
    /// string parentPath;
    /// string currentDirectoryName = adapter.GetDirectoryName("c:\Windows", out parentPath);
    /// //parentPath: "c:\"
    /// //currentDirectoryName: "Windows"
    /// </example>
    string GetDirectoryName(string path, out string parentPath);

    /// <summary>
    /// Returns a list of directories found in the specified <paramref name="targetDirectory"/>
    /// </summary>
    /// <param name="targetDirectory">the directory to process</param>
    /// <returns>an array of directories found</returns>
    Directory[] GetExistingDirectories(DirectoryContext targetDirectory);
  }
}